package com.edev.support.ddd.utils;

import com.edev.support.ddd.DddException;
import com.edev.support.dsl.DomainObject;
import com.edev.support.dsl.DomainObjectFactory;
import com.edev.support.entity.Entity;
import com.edev.support.utils.BeanUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 领域实体工具类（DDD核心工具）
 * <p>
 * 核心功能：
 * 1. 实体类型检查与验证
 * 2. 实体继承关系处理
 * 3. 实体集合差异比较
 * 4. 领域对象元数据操作
 */
public class EntityUtils {
    private EntityUtils() {}
    /**
     * 检查该类是否是领域实体
     * @param clazz 待检查的类对象
     * @return true-是已注册实体类
     * @throws DddException 当类继承Entity但未注册时抛出
     */
    public static boolean isEntity(Class<?> clazz) {
        if(clazz==null||Entity.class.equals(clazz)) return false;
        if(Entity.class.isAssignableFrom(clazz)) {
            if (DomainObjectFactory.isExists(clazz.getName())) return true;
            if(clazz.getSuperclass()!=null&&DomainObjectFactory.isExists(clazz.getSuperclass().getName())) return true;
            throw new DddException("The entity[%s] isn't register in domain objects register files.", clazz.getName());
        }
        return false;
    }

    /**
     * 判断类是否为实体子类
     * @param clazz 待检查的类对象
     * @return true-是实体子类（非Entity基类）
     */
    public static boolean isSubClass(Class<?> clazz) {
        return isEntity(clazz)&&!clazz.getSuperclass().equals(Entity.class);
    }

    /**
     * 通过类名检查该类是否是领域实体
     * @param className 待检查的类名
     * @return true-是已注册实体类
     * @throws DddException 当类继承Entity但未注册时抛出
     */
    public static boolean isEntity(String className) {
        return isEntity(getClassOfEntity(className));
    }

    /**
     * 检查该类是否有子类
     * @param clazz 待检查的类对象
     * @return true-有子类
     */
    public static boolean hasSubClass(Class<?> clazz) {
        if(!isEntity(clazz)) return false;
        return hasSubClass(clazz.getName());
    }

    /**
     * 通过类名检查该类是否有子类
     * @param className 待检查的类名
     * @return true-有子类
     */
    public static boolean hasSubClass(String className) {
        DomainObject dObj = DomainObjectFactory.getDomainObject(className);
        return hasSubClass(dObj);
    }

    /**
     * 通过领域对象元数据检查该类是否有子类
     * @param dObj 待检查的领域对象元数据
     * @return true-有子类
     */
    public static boolean hasSubClass(DomainObject dObj) {
        String subClassType = dObj.getSubClassType();
        return subClassType!=null&&!subClassType.isEmpty();
    }

    /**
     * 通过类名获取该实体的类对象
     * @param className 实体类名（示例："com.edev.trade.Customer"）
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @throws DddException 当类不是实体时抛出异常
     * @return 实体类的Class对象
     */
    @SuppressWarnings("unchecked")
    public static <E extends Entity<S>, S extends Serializable> Class<E> getClassOfEntity(String className) {
        Class<?> clazz = BeanUtils.getClazz(className);
        if(!Entity.class.isAssignableFrom(clazz))
            throw new DddException("The class is not an entity: [%s]", className);
        return (Class<E>) clazz;
    }

    /**
     * 通过实体对象获取该实体的类对象
     * @param entity 实体对象
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 实体类的Class对象
     */
    @SuppressWarnings("unchecked")
    public static <E extends Entity<S>, S extends Serializable> Class<E> getClass(E entity) {
        return (Class<E>) entity.getClass();
    }

    /**
     * 通过实体对象获取该实体的父类的类对象
     * @param entity 实体对象
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 实体类的父类的Class对象
     */
    @SuppressWarnings("unchecked")
    public static <E extends Entity<S>, S extends Serializable> Class<E> getSuperclass(E entity) {
        return (Class<E>) entity.getClass().getSuperclass();
    }

    /**
     * 通过实体类的Class对象获取该实体的父类的类对象
     * @param clazz 实体类的Class对象
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 实体类的父类的Class对象
     */
    @SuppressWarnings("unchecked")
    public static <E extends Entity<S>, S extends Serializable> Class<E> getSuperclass(Class<E> clazz) {
        return (Class<E>)clazz.getSuperclass();
    }

    /**
     * 比较两个实体集合的差异，返回差异结果，包含：新增、更新、删除的实体集合，
     * 以及回调函数，用于对差异结果进行进一步处理。
     * @param source 源实体集合
     * @param target 目标实体集合
     * @param callback 回调函数，用于对差异结果进行进一步处理
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    public static <E extends Entity<S>, S extends Serializable>
            void compareListOrSetOfEntity(Collection<E> source, Collection<E> target, ComparedCollectionCallback<E> callback) {
        compareListOrSetOfEntity(source, target, (sourceEntity, targetEntity)-> targetEntity.getId().equals(sourceEntity.getId()), callback);
    }

    /**
     * 比较两个实体集合的差异，返回差异结果，包含：新增、更新、删除的实体集合，
     * 以及回调函数，用于对差异结果进行进一步处理。
     * @param source 源实体集合
     * @param target 目标实体集合
     * @param matchKeyCallback 自定义主键匹配回调函数，用于判断两个实体是否为同一实体
     * @param callback 回调函数，用于对差异结果进行进一步处理
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    public static <E extends Entity<S>, S extends Serializable>
            void compareListOrSetOfEntity(Collection<E> source, Collection<E> target,
                                          MatchKeyCallback<E> matchKeyCallback, ComparedCollectionCallback<E> callback) {
        if(source==null) source = new ArrayList<>();
        if(target==null) target = new ArrayList<>();

        List<E> inserted = new ArrayList<>();
        List<E> updated = new ArrayList<>();
        List<E> deleted = new ArrayList<>(source);
        for(E targetEntity : target) {
            boolean isMatched = false;
            for (E sourceEntity : source) {
                if(matchKeyCallback.isKeyMatch(sourceEntity, targetEntity)) {
                    isMatched = true;
                    if(!targetEntity.equals(sourceEntity))
                        updated.add(targetEntity);
                    break;
                }
            }
            if(!isMatched) inserted.add(targetEntity);
            for (int i=0; i<deleted.size(); i++)
                if(matchKeyCallback.isKeyMatch(deleted.get(i), targetEntity)) {
                    deleted.remove(i);
                    break;
                }
        }
        callback.apply(inserted, updated, deleted);
    }

    /**
     * 自定义主键匹配回调接口（函数式接口）
     * @param <E> 实体类型
     */
    @FunctionalInterface
    public interface MatchKeyCallback<E> {
        boolean isKeyMatch(E sourceEntity, E targetEntity);
    }

    /**
     * 集合差异处理回调接口（函数式接口）
     * @param <E> 实体类型
     */
    @FunctionalInterface
    public interface ComparedCollectionCallback<E> {
        void apply(List<E> inserted, List<E> updated, List<E> deleted);
    }
}
